#include "protocol.h"

bool DataPackage::buildFrame(cmd_t data, QByteArray* outBuf)
{
    quint16 frameLength = 0;

    switch(data.command)
    {
    case CMD_PUSH_POWER_DATA:
        if((frameLength = buildHeader(outBuf, CMD_PUSH_POWER_DATA, data.deviceID, data.frameNum))!=0)
        {
            quint8 offset = 0; //data.pushPowerData.data;

            // LOG KIND
            (*outBuf)[DATA_FRAME_START_POS+offset] = DATA_FRAME_PUSH_LOG_KIND_ONE;
            offset++;
            (*outBuf)[DATA_FRAME_START_POS+offset] = DATA_FRAME_PUSH_LOG_KIND_TWO;
            offset++;
            // LOG ID
            (*outBuf)[DATA_FRAME_START_POS+offset] = data.pushPowerData.channelNum;
            offset++;
            (*outBuf)[DATA_FRAME_START_POS+offset] = 0x00;
            offset++;
            // LOG POWER DATA LENGTH
            (*outBuf)[DATA_FRAME_START_POS+offset] = DATA_POWERDATA_FRAME_PUSH_LOG_LENGTH;
            offset++;
            (*outBuf)[DATA_FRAME_START_POS+offset] = 0x00;
            offset++;
            (*outBuf)[DATA_FRAME_START_POS+offset] = 0x00;
            offset++;
            (*outBuf)[DATA_FRAME_START_POS+offset] = 0x00;
            offset++;

            // POWER DATA Channel Num
            (*outBuf)[DATA_FRAME_START_POS+offset] = (data.pushPowerData.data & 0x000000FF);
            offset++;
            (*outBuf)[DATA_FRAME_START_POS+offset] = (data.pushPowerData.data & 0x0000FF00)>>8;
            offset++;
            (*outBuf)[DATA_FRAME_START_POS+offset] = (data.pushPowerData.data & 0x00FF0000)>>16;
            offset++;
            (*outBuf)[DATA_FRAME_START_POS+offset] = (data.pushPowerData.data & 0xFF000000)>>24;
            offset++;

            // 끝문자
            (*outBuf)[DATA_FRAME_START_POS+offset] = HEADER_FRAME_END;
            offset++;

            frameLength += offset;
            //  + checksum
            frameLength += 1;
            // Set Length
            (*outBuf)[HEADER_FRAME_LENGTH_POS] = frameLength & 0x00FF;
            (*outBuf)[HEADER_FRAME_LENGTH_POS+1] = (frameLength & 0xFF00) >> 8;

//            char* temp = (*outBuf).data();
//            int length = (*outBuf).size() - 3; // Frame start 2 bytes + Frame end 1 byte
//            quint16 qchecksum = modbusCrc((unsigned char*)(temp+2),length);
            quint16 qchecksum = 0;
            for (int i = 2; i < (*outBuf).size()-1; i++)
                qchecksum += (*outBuf)[i];

            // Checksum
            (*outBuf)[DATA_FRAME_START_POS+offset] =(quint8)(qchecksum & 0x00FF);
            return true;
        }
        break;
    case CMD_SEND_ACKNAK:
        if((frameLength = buildHeader(outBuf, CMD_SEND_ACKNAK, data.deviceID, data.frameNum))!=0)
        {
            quint16 reason = data.acknakData.data;

            // ERROR
            (*outBuf)[DATA_FRAME_START_POS] = reason & 0x00FF;
            (*outBuf)[DATA_FRAME_START_POS+1] = (reason & 0xFF00)>> 8;
            frameLength += 2; //Data Length
            // 끝문자
            (*outBuf)[DATA_FRAME_START_POS+2] = HEADER_FRAME_END;
            frameLength += 1;
            //  + checksum
            frameLength += 1;
            // Set Length
            (*outBuf)[HEADER_FRAME_LENGTH_POS] = frameLength & 0x00FF;
            (*outBuf)[HEADER_FRAME_LENGTH_POS+1] = (frameLength & 0xFF00) >> 8;

//            char* temp = (*outBuf).data();
//            int length = (*outBuf).size() - 3; // Frame start 2 bytes + Frame end 1 byte
//            quint16 qchecksum = modbusCrc((unsigned char*)(temp+2),length);
            quint16 qchecksum = 0;
            for (int i = 2; i < (*outBuf).size()-1; i++)
                qchecksum += (*outBuf)[i];

            // Checksum
            (*outBuf)[DATA_FRAME_START_POS+3] =(quint8)(qchecksum & 0x00FF);

            return true;
        }
        break;
    case CMD_SEND_CHECK_DEVICE:
        if((frameLength = buildHeader(outBuf, CMD_SEND_CHECK_DEVICE, 0xFFFF, data.frameNum))!=0)
        {
            (*outBuf)[DATA_FRAME_START_POS] = 0x3F;
            frameLength += 1; //Data command
            // 끝문자
            (*outBuf)[DATA_FRAME_START_POS+1] = HEADER_FRAME_END;
            frameLength += 1;
            //  + checksum
            frameLength += 1;
            // Set Length
            (*outBuf)[HEADER_FRAME_LENGTH_POS] = frameLength & 0x00FF;
            (*outBuf)[HEADER_FRAME_LENGTH_POS+1] = (frameLength & 0xFF00) >> 8;

//            char* temp = (*outBuf).data();
//            int length = (*outBuf).size() - 3; // Frame start 2 bytes + Frame end 1 byte
//            quint16 qchecksum = modbusCrc((unsigned char*)(temp+2),length);
            quint16 qchecksum = 0;
            for (int i = 2; i < (*outBuf).size()-1; i++)
                qchecksum += (*outBuf)[i];
            // Checksum
            (*outBuf)[DATA_FRAME_START_POS+2] =(quint8)(qchecksum & 0x00FF);

            return true;
        }
        break;
    default:
        break;
    }
    return false;
}

quint16 DataPackage::buildHeader(QByteArray* outBuf, COMMAND cmd, quint16 deviceID, quint8 frameNum)
{
    quint16 frameLength = 0;

    // 시작 문자
    (*outBuf)[HEADER_FRAME_START_POS] = HEADER_FRAME_START_ONE;
    (*outBuf)[HEADER_FRAME_START_POS+1] = HEADER_FRAME_START_TWO;
    // 국번
    (*outBuf)[HEADER_FRAME_DEVICEID_POS] = (deviceID & 0x00FF);
    (*outBuf)[HEADER_FRAME_DEVICEID_POS+1] = (deviceID & 0xFF00) >> 8;
    frameLength += 2;
    // 서브국번
    (*outBuf)[HEADER_FRAME_SUBDEVICEID_POS] = 0x00;
    (*outBuf)[HEADER_FRAME_SUBDEVICEID_POS+1] = 0x00;
    frameLength += 2;
    // App 일련 번호
    (*outBuf)[HEADER_FRAME_APPID_POS] = 0x00;
    (*outBuf)[HEADER_FRAME_APPID_POS+1] = 0x00;
    frameLength += 2;

    switch(cmd)
    {
    case CMD_PUSH_POWER_DATA: // Data frame header : Push
        // 컨트롤 필드
        (*outBuf)[HEADER_FRAME_CONTROL_POS] = HEADER_FRAME_CONTROL_PUSH|HEADER_FRAME_CONTROL_FIR|HEADER_FRAME_CONTROL_FIN;
        frameLength += 1;
        // Frame 일련 번호
        (*outBuf)[HEADER_FRAME_FRAMENO_POS] = frameNum;
        frameLength += 1;
        break;
    case CMD_SEND_CHECK_DEVICE:
        // 컨트롤 필드
        (*outBuf)[HEADER_FRAME_CONTROL_POS] = HEADER_FRAME_CONTROL_FIR|HEADER_FRAME_CONTROL_FIN;
        frameLength += 1;
        // Frame 일련 번호
        (*outBuf)[HEADER_FRAME_FRAMENO_POS] = frameNum;
        frameLength += 1;
        break;
    case CMD_SEND_ACKNAK: // ACK/NAK frame header
         // 길이
//        (*outBuf)[2] = HEADER_ACKNAK_FRAME_LENGTH_0;
//        (*outBuf)[3] = HEADER_ACKNAK_FRAME_LENGTH_1;
        // 컨트롤 필드
        (*outBuf)[HEADER_FRAME_CONTROL_POS] = HEADER_FRAME_CONTROL_ACKNAK|HEADER_FRAME_CONTROL_FIR|HEADER_FRAME_CONTROL_FIN;
        frameLength += 1;
        // Frame 일련 번호
        (*outBuf)[HEADER_FRAME_FRAMENO_POS] = frameNum;
        frameLength += 1;
        // 데이타 영역
//        (*outBuf)[DATA_FRAME_START_POS] = 0;
//        (*outBuf)[DATA_FRAME_START_POS+1] = 0;
//        frameLength += 2;
        // 끝문자
//        (*outBuf)[DATA_FRAME_START_POS+2] = HEADER_FRAME_END;
//        frameLength += 1;
        break;
     default:
        frameLength = 0;
        break;
    }
    return frameLength;
}

RET_ERROR DataPackage::parseACKNAK(cmd_t rcvdata, QByteArray block, ERROR_ACKNAK* cause)
{
    // Check Packet header
    if((block[HEADER_FRAME_START_POS] != HEADER_FRAME_START_ONE) || (block[HEADER_FRAME_START_POS+1] != HEADER_FRAME_START_TWO))
        return ERROR_RECEIVE_INVALID_START;

    // Check ACKNAK frame length
    quint16 frameLength = 0;
    frameLength = block[HEADER_FRAME_LENGTH_POS] | (quint16)(block[HEADER_FRAME_LENGTH_POS+1] << 8);
    if((frameLength + 4) != block.size())
        return ERROR_RECEIVE_MISMATCH_FRAME_LENGTH;

    // Check ACKNAK Control bit
    if((block[HEADER_FRAME_CONTROL_POS] & HEADER_FRAME_CONTROL_ACKNAK) != HEADER_FRAME_CONTROL_ACKNAK)
        return ERROR_RECEIVE_INVALID_FRAME;

    // Check CRC
//    char* temp = block.data();
//    int length = block.size() - 4;
//    quint16 qchecksum = modbusCrc((unsigned char*)(temp+2),length);
    quint16 qchecksum = 0;
    for(int i = 2; i < block.size()-2; i++)
        qchecksum += block[i];

    if(block[block.size()-1] != (quint8)(qchecksum & 0x00FF))
        return ERROR_RECEIVE_INVALID_CHECKSUM;

    // Check deviceID
    if(rcvdata.deviceID != (block[HEADER_FRAME_DEVICEID_POS] | (block[HEADER_FRAME_DEVICEID_POS+1] << 8)))
        return ERROR_RECEIVE_MISMATCH_DEVICEID;

    // Check frameNo
    if(rcvdata.frameNum != (quint8) block[HEADER_FRAME_FRAMENO_POS])
        return ERROR_RECEIVE_MISMATCH_FRAMENO;

    // Get ACKNAK cause
    quint16 ACKNAKcause = block[12] | (quint16)(block[13] << 8);

    switch(ACKNAKcause)
    {
    case 0:
        *cause = ACKNAK_NO_ERROR;
        break;
    default:
        *cause = ERROR_OTHER;
    }
    return NO_ERROR;
}

RET_ERROR DataPackage::parseCheckDEVICEID(QByteArray block)
{
    // Check Packet header
    if((block[HEADER_FRAME_START_POS] != HEADER_FRAME_START_ONE) || (block[HEADER_FRAME_START_POS+1] != HEADER_FRAME_START_TWO))
        return ERROR_RECEIVE_INVALID_START;

    // Check frame length
    quint16 frameLength = 0;
    frameLength = block[HEADER_FRAME_LENGTH_POS] | (quint16)(block[HEADER_FRAME_LENGTH_POS+1] << 8);
    if((frameLength + 4) != block.size())
        return ERROR_RECEIVE_MISMATCH_FRAME_LENGTH;

    // Check Control bit
    if(block[HEADER_FRAME_CONTROL_POS] != (HEADER_FRAME_CONTROL_FIN | HEADER_FRAME_CONTROL_FIR))
        return ERROR_RECEIVE_INVALID_FRAME;

    // Check CRC
//    char* temp = block.data();
//    int length = block.size() - 4;
//    quint16 qchecksum = modbusCrc((unsigned char*)(temp+2),length);
    quint16 qchecksum = 0;
    for(int i = 2; i < block.size()-2; i++)
        qchecksum += block[i];

    if(block[block.size()-1] != (quint8)(qchecksum & 0x00FF))
        return ERROR_RECEIVE_INVALID_CHECKSUM;

    // Check deviceID
    if(0xFFFF != (quint16)(block[HEADER_FRAME_DEVICEID_POS] | (block[HEADER_FRAME_DEVICEID_POS+1] << 8)))
        return ERROR_RECEIVE_MISMATCH_DEVICEID;

    // Check DEVICE ID Command
    if(block[DATA_FRAME_START_POS] != (quint8) 0x3F)
        return ERROR_RECEIVE_INVALID_COMMAND;

    return NO_ERROR;
}

RET_ERROR DataPackage::parseACKNAK_deviceID(cmd_t rcvdata, QByteArray block, quint16* deviceID)
{
    // Check Packet header
    if((block[HEADER_FRAME_START_POS] != HEADER_FRAME_START_ONE) || (block[HEADER_FRAME_START_POS+1] != HEADER_FRAME_START_TWO))
        return ERROR_RECEIVE_INVALID_START;

    // Check ACKNAK frame length
    quint16 frameLength = 0;
    frameLength = block[HEADER_FRAME_LENGTH_POS] | (quint16)(block[HEADER_FRAME_LENGTH_POS+1] << 8);
    if((frameLength + 4) != block.size())
        return ERROR_RECEIVE_MISMATCH_FRAME_LENGTH;

    // Check ACKNAK Control bit
    if((block[HEADER_FRAME_CONTROL_POS] & HEADER_FRAME_CONTROL_ACKNAK) != HEADER_FRAME_CONTROL_ACKNAK)
        return ERROR_RECEIVE_INVALID_FRAME;

    // Check CRC
//    char* temp = block.data();
//    int length = block.size() - 4;
//    quint16 qchecksum = modbusCrc((unsigned char*)(temp+2),length);
    quint16 qchecksum = 0;
    for(int i = 2; i < block.size()-2; i++)
        qchecksum += block[i];

    if(block[block.size()-1] != (quint8)(qchecksum & 0x00FF))
        return ERROR_RECEIVE_INVALID_CHECKSUM;


    // Check frameNo
    if(rcvdata.frameNum != (quint8) block[HEADER_FRAME_FRAMENO_POS])
        return ERROR_RECEIVE_MISMATCH_FRAMENO;

    // Get ACKNAK cause
    quint16 ACKNAKcause = block[12] | (quint16)(block[13] << 8);

    switch(ACKNAKcause)
    {
    case 0:
        // return Check deviceID
        *deviceID = (block[HEADER_FRAME_DEVICEID_POS] | (block[HEADER_FRAME_DEVICEID_POS+1] << 8));
        break;
    default:
        return ERROR_RECEIVE_INVALID_FRAME;
    }
    return NO_ERROR;
}

ERROR_ACKNAK DataPackage::convert2ACKNAKerror(RET_ERROR error)
{
    ERROR_ACKNAK ret = ACKNAK_NO_ERROR;

    switch(error)
    {
    case NO_ERROR:
        break;
    case ERROR_RECEIVE_MISMATCH_FRAME_LENGTH:
        ret = ERROR_DEVICE_MISMATCH_DATA_LENGTH;
        break;
    case ERROR_RECEIVE_INVALID_CHECKSUM:
        ret = ERROR_INVALIDE_CHECKSUM;
        break;
    case ERROR_RECEIVE_MISMATCH_FRAMENO:
        ret = ERROR_MISMATCH_FRAME_NO;
        break;
    case ERROR_RECEIVE_INVALID_FRAME:
    case ERROR_RECEIVE_INVALID_COMMAND:
        ret = ERROR_COMMAND;
        break;
    case ERROR_RECEIVE_MISMATCH_DEVICEID:
    default:
        ret = ERROR_OTHER;
        break;
    }

    return ret;
}

unsigned short DataPackage::modbusCrc(unsigned char *data, int length)
{
    static const unsigned short tableCRC[] = {
        0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
        0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
        0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
        0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
        0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
        0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
        0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
        0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
        0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
        0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
        0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
        0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
        0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
        0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
        0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
        0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
        0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
        0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
        0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
        0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
        0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
        0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
        0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
        0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
        0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
        0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
        0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
        0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
        0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
        0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
        0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
        0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
    };

    unsigned char idxCRC;
    unsigned short wordCRC = 0xFFFF;

    while (length--) {
        idxCRC = *data++ ^ wordCRC;
        wordCRC >>= 8;
        wordCRC ^= tableCRC[idxCRC];
    }

    return wordCRC;
}
